// openfile.cc 
//	Routines to manage an open Nachos file.  As in UNIX, a
//	file must be open before we can read or write to it.
//	Once we're all done, we can close it (in Nachos, by deleting
//	the OpenFile data structure).
//
//	Also as in UNIX, for convenience, we keep the file header in
//	memory while the file is open.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "filehdr.h"
#include "openfile.h"
#include "system.h"

//----------------------------------------------------------------------
// OpenFile::OpenFile
// 	Open a Nachos file for reading and writing.  Bring the file header
//	into memory while the file is open.
//
//	"sector" -- the location on disk of the file header for this file
//----------------------------------------------------------------------

OpenFile::OpenFile(int sector)
{ 
    // 打开文件之后 要记录FCB 块的位置
    hdrSector = sector;

    hdr = new FileHeader;
    hdr->FetchFrom(sector);
    seekPosition = 0;
}

//----------------------------------------------------------------------
// OpenFile::~OpenFile
// 	Close a Nachos file, de-allocating any in-memory data structures.
//----------------------------------------------------------------------

OpenFile::~OpenFile()
{
    delete hdr;
}

//----------------------------------------------------------------------
// OpenFile::Seek
// 	Change the current location within the open file -- the point at
//	which the next Read or Write will start from.
//
//	"position" -- the location within the file for the next Read/Write
//----------------------------------------------------------------------
// 重定位当前读写指针的位置， 这个指针是nachos文件的指针，不是UNIX文件指针
void
OpenFile::Seek(int position)
{
    seekPosition = position;
}	

//----------------------------------------------------------------------
// OpenFile::Read/Write
// 	Read/write a portion of a file, starting from seekPosition.
//	Return the number of bytes actually written or read, and as a
//	side effect, increment the current position within the file.
//
//	Implemented using the more primitive ReadAt/WriteAt.
//
//	"into" -- the buffer to contain the data to be read from disk 
//	"from" -- the buffer containing the data to be written to disk 
//	"numBytes" -- the number of bytes to transfer
//----------------------------------------------------------------------

int
OpenFile::Read(char *into, int numBytes)
{
   int result = ReadAt(into, numBytes, seekPosition);
   seekPosition += result;
   return result;
}

int
OpenFile::Write(char *into, int numBytes)
{
    //DEBUG

   // printf("OpenFile::Write  numBytes %d,seekposition %d\n",numBytes,seekPosition);
   int result = WriteAt(into, numBytes, seekPosition);
   seekPosition += result;
   return result;
}

//----------------------------------------------------------------------
// OpenFile::ReadAt/WriteAt
// 	Read/write a portion of a file, starting at "position".
//	Return the number of bytes actually written or read, but has
//	no side effects (except that Write modifies the file, of course).
//
//	There is no guarantee the request starts or ends on an even disk sector
//	boundary; however the disk only knows how to read/write a whole disk
//	sector at a time.  Thus:
//
//	For ReadAt:
//	   We read in all of the full or partial sectors that are part of the
//	   request, but we only copy the part we are interested in.
//	For WriteAt:
//	   We must first read in any sectors that will be partially written,
//	   so that we don't overwrite the unmodified portion.  We then copy
//	   in the data that will be modified, and write back all the full
//	   or partial sectors that are part of the request.
//
//	"into" -- the buffer to contain the data to be read from disk 
//	"from" -- the buffer containing the data to be written to disk 
//	"numBytes" -- the number of bytes to transfer
//	"position" -- the offset within the file of the first byte to be
//			read/written
//----------------------------------------------------------------------

int
OpenFile::ReadAt(char *into, int numBytes, int position)
{
    int fileLength = hdr->FileLength();
    int i, firstSector, lastSector, numSectors;
    char *buf;

    if ((numBytes <= 0) || (position >= fileLength))
    	return 0; 				// check request
    if ((position + numBytes) > fileLength)		
	numBytes = fileLength - position;
    

    firstSector = divRoundDown(position, SectorSize);
    lastSector = divRoundDown(position + numBytes - 1, SectorSize);
    numSectors = 1 + lastSector - firstSector;
    DEBUG('f', "Openfile ::ReadAt Read from %dth sector to %dth, %d sectors intotal.\n", 	
			firstSector, lastSector, numSectors);
    // read in all the full and partial sectors that we need
    buf = new char[numSectors * SectorSize];
    for (i = firstSector; i <= lastSector; i++)	
        synchDisk->ReadSector(hdr->ByteToSector(i * SectorSize), 
					&buf[(i - firstSector) * SectorSize]);

    // copy the part we want
    bcopy(&buf[position - (firstSector * SectorSize)], into, numBytes);
    delete [] buf;
    return numBytes;
}

int
OpenFile::WriteAt(char *from, int numBytes, int position)
{
    int fileLength = hdr->FileLength();
    int i, firstSector, lastSector, numSectors;
    bool firstAligned, lastAligned;
    char *buf;

    // printf("OpenFile::WriteAt fileLength%d numBytes %d,seekposition %d\n",
     //fileLength,numBytes,position);
    //1、 检查要写入的位置 ,position 最多只能在文件最末的位置进行写入 fileLength 是最后可以写入的位置
    if((numBytes <=0) || (position > fileLength))
        return -1;
    //2、 检查是否要拓展文件长度
    if ((position + numBytes) > fileLength){
        DEBUG('f',"--Need to extend the size of nachos file--\n");

        // DEBUG
       // printf("--Need to extend the size of nachos file--\n");
        // 如果需要进行文件扩展
        int incrementBytes = (position + numBytes) - fileLength;
        BitMap *freeBitMap = fileSystem-> getBitMap();
        //自己实现
        bool hdrRet; // 标记分配块是否成功
        hdrRet = hdr->Reallocate(freeBitMap, fileLength, incrementBytes); //自己实现
        if ( !hdrRet ) // Insuficient Disk Space, or File is Too Big
            return -1;

        fileSystem-> setBitMap(freeBitMap); //自己实现
        // 重新获得文件长度并输出
        fileLength = hdr->FileLength();
        DEBUG('f', "Writing %d bytes at %d, from file of length %d.\n", 	
			numBytes, position, fileLength);
    }
    
    

    firstSector = divRoundDown(position, SectorSize);
    lastSector = divRoundDown(position + numBytes - 1, SectorSize);
    numSectors = 1 + lastSector - firstSector;
    buf = new char[numSectors * SectorSize];

    firstAligned = (bool)(position == (firstSector * SectorSize));
    lastAligned = (bool)((position + numBytes) == ((lastSector + 1) * SectorSize));

// read in first and last sector, if they are to be partially modified
    if (!firstAligned)
        ReadAt(buf, SectorSize, firstSector * SectorSize);	
    if (!lastAligned && ((firstSector != lastSector) || firstAligned))
        ReadAt(&buf[(lastSector - firstSector) * SectorSize], 
				SectorSize, lastSector * SectorSize);	

// copy in the bytes we want to change 
    bcopy(from, &buf[position - (firstSector * SectorSize)], numBytes);

// write modified sectors back    写回磁盘
    // 将内容向磁盘文件写的时候， hdr->ByteToSector(i * SectorSize) 返回写入的块号
    // 因此修改 hdr->ByteToSector() 函数
    
    for (i = firstSector; i <= lastSector; i++)	
        synchDisk->WriteSector(hdr->ByteToSector(i * SectorSize), 
					&buf[(i - firstSector) * SectorSize]);
    WriteBack();
    delete [] buf;
    return numBytes;
}

//----------------------------------------------------------------------
// OpenFile::Length
// 	Return the number of bytes in the file.
//----------------------------------------------------------------------

int
OpenFile::Length() 
{ 
    return hdr->FileLength(); 
}



//----------------------------------------------------------------------
// OpenFile::WriteBack
// 	将fielheader 写回磁盘
//----------------------------------------------------------------------

void 
OpenFile::WriteBack(){
    hdr->WriteBack(hdrSector);
}


// 重载构造函数
OpenFile::OpenFile(char*type){}


//----------------------------------------------------------------------
// OpenFile::WriteStdout
//  模拟标准输出，底层调用UNIX
//----------------------------------------------------------------------

int OpenFile::WriteStdout(char* buf,int numBytes){
		// 直接调用 UNIX系统调用向屏幕输出
		WriteFile(1,buf, numBytes);
}

//----------------------------------------------------------------------
// OpenFile::ReadStdin
//  模拟标准输入，底层调用UNIX
//----------------------------------------------------------------------

int
OpenFile::ReadStdin(char*buf ,int numBytes) {
    // 将结果写入 buf
    // 会阻塞等待输入完成
    return ReadPartial(0, buf, numBytes);
}